﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;

namespace ESpace
{
    public class BufReader
    {
        int n = 0;
        byte[] buf;
        public BufReader(byte[] buf)
        {
            this.buf = buf;
        }

        public void New(byte[] buf)
        {
            this.buf = buf;
            n = 0;
        }

        public byte ReadByte()
        {
            if (n >= buf.Length) throw new Exception("Ошибка разбора пакета: Ошибка чтения байта");
            n++;
            return buf[n - 1];
        }

        public short ReadShort()
        {
            try
            {
                if ((n + 2) > buf.Length) return 0;
                short a = (short)((buf[n] << 8) + buf[n + 1]);
                n += 2;
                return a;
            }
            catch (Exception ex) { throw new Exception("Ошибка разбора пакета: Ошибка чтения Short >> " + ex.Message); }
        }

        public int ReadInt()
        {
            try
            {
                if ((n + 4) > buf.Length) return 0;
                int a = 0;
                for (int i = 0; i < 4; i++)
                    a += buf[n + i] << ((3 - i) * 8);
                n += 4;
                return a;
            }
            catch (Exception ex) { throw new Exception("Ошибка разбора пакета: Ошибка чтения Int >> " + ex.Message); }
        }

        public long ReadLong()
        {
            try
            {
                if ((n + 8) > buf.Length) return 0;
                long a = (uint)ReadInt();
                long b = (uint)ReadInt();
                return (a * (long)(0x100000000) + b);
            }
            catch (Exception ex) { throw new Exception("Ошибка разбора пакета: Ошибка чтения Long >> " + ex.Message); }
        }

        public float ReadFloat()
        {
            try
            {
                if ((n + 4) > buf.Length) return 0;
                byte[] b = new byte[4];
                for (int i = 0; i < 4; i++)
                    b[i] = buf[n + i];
                n += 4;
                IntPtr ptr1 = System.Runtime.InteropServices.Marshal.UnsafeAddrOfPinnedArrayElement(b, 0);
                return (float)System.Runtime.InteropServices.Marshal.PtrToStructure(ptr1, typeof(float));
            }
            catch (Exception ex) { throw new Exception("Ошибка разбора пакета: Ошибка чтения Float >> " + ex.Message); }
        }

        public double ReadDouble()
        {
            try
            {
                if ((n + 8) > buf.Length) return 0;
                byte[] b = new byte[8];
                for (int i = 0; i < 8; i++)
                    b[i] = buf[n + i];
                n += 8;
                IntPtr ptr1 = System.Runtime.InteropServices.Marshal.UnsafeAddrOfPinnedArrayElement(b, 0);
                return (double)System.Runtime.InteropServices.Marshal.PtrToStructure(ptr1, typeof(double));
            }
            catch (Exception ex) { throw new Exception("Ошибка разбора пакета: Ошибка чтения Double >> " + ex.Message); }
        }

        public DateTime ReadDateTime()
        {
            try
            {
                if ((n + 8) > buf.Length) return DateTime.MinValue;
                return new DateTime(ReadLong());
            }
            catch (Exception ex) { throw new Exception("Ошибка разбора пакета: Ошибка чтения DateTime >> " + ex.Message); }
        }

        public String ReadString()
        {
            try
            {
                byte[] buf = ReadMas();
                String s = "";
                try
                {
                    s = Encoding.Unicode.GetString(buf);
                }
                catch (Exception ex) { throw new Exception("Ошибка разбора пакета: Ошибка чтения строки >> " + ex.Message); }
                return s;
            }
            catch (Exception ex) { throw new Exception("Ошибка разбора пакета: Ошибка чтения строки >> " + ex.Message); }

        }

        public String ReadStringASCII()
        {
            try
            {
                byte[] buf = ReadMas();
                String s = "";
                try
                {
                    s = Encoding.ASCII.GetString(buf);
                }
                catch (Exception ex) { throw new Exception("Ошибка разбора пакета: Ошибка чтения строки ASCII >> " + ex.Message); }
                return s;
            }
            catch (Exception ex) { throw new Exception("Ошибка разбора пакета: Ошибка чтения строки ASCII >> " + ex.Message); }

        }

        public byte[] ReadMas()
        {
            try
            {
                if ((n + 4) > buf.Length) return null;
                int len = ReadInt();
                if (len < 0) return null;
                if ((n + len) > buf.Length) return null;
                byte[] b = new byte[len];
                for (int i = 0; i < len; i++)
                {
                    b[i] = buf[n + i];
                }
                n += len;
                return b;
            }
            catch (Exception ex) { throw new Exception("Ошибка разбора пакета: Ошибка чтения массива байт >> " + ex.Message); }
        }

        public bool[] ReadBitMas()
        {
            try
            {
                if ((n + 4) > buf.Length) return null;
                int len = ReadInt();
                if (len < 0) return null;
                if (len % 8 == 0)
                {
                    if ((n + len / 8) > buf.Length) return null;
                }
                else
                {
                    if ((n + len / 8 + 1) > buf.Length) return null;
                }

                bool[] b = new bool[len];
                int razr = 1; // разряд
                for (int i = 0; i < len; i++)
                {
                    b[i] = (buf[n + i / 8] & razr) != 0;
                    razr *= 2;
                    if (razr == 256) razr = 1;
                }

                if (len % 8 == 0) n += len / 8;
                else n += len / 8 + 1;

                return b;
            }
            catch (Exception ex) { throw new Exception("Ошибка разбора пакета: Ошибка чтения бит-массива >> " + ex.Message); }
        }

        public Bitmap ReadBmp()
        {
            try
            {
                int pW = ReadShort();
                int pH = ReadShort();
                Bitmap bmp = new Bitmap(pW, pH);

                // Блокирует битмап без копирования 
                Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
                BitmapData bmpData = bmp.LockBits(rect, ImageLockMode.ReadWrite, bmp.PixelFormat);
                // Get the address of the first line.
                IntPtr ptr = bmpData.Scan0;

                // читаем
                byte[] rgbValues = ReadMas();

                // копируем в битмап
                if (rgbValues != null)
                {
                    System.Runtime.InteropServices.Marshal.Copy(rgbValues, 0, ptr, rgbValues.Length);
                }
                // разблокируем битмап
                bmp.UnlockBits(bmpData);

                return bmp;
            }
            catch (Exception ex) { throw new Exception("Ошибка разбора пакета: Ошибка чтения Bmp >> " + ex.Message); }
        }

    }

    public class BufWriter
    {

        public static BufWriter operator +(BufWriter b1, BufWriter b2)
        {
            if (b1 == null & b2 == null) return null;
            BufWriter bw = new BufWriter();
            if (b1 != null)
                for (int i = 0; i < b1.bl.Count; i++)
                    bw.bl.Add(b1.bl[i]);
            if (b2 != null)
                for (int i = 0; i < b2.bl.Count; i++)
                    bw.bl.Add(b2.bl[i]);
            return bw;
        }

        List<byte> bl = new List<byte>();

        public byte[] buf { get { return bl.ToArray(); } }

        public void AddBufWriterMas(byte[] buf)
        {
            for (int i = 0; i < buf.Length; i++)
                bl.Add(buf[i]);
        }

        public BufWriter()
        {
        }

        public void New()
        {
            bl.Clear();
        }

        public void WriteByte(byte b) { bl.Add(b); }

        public void WriteShort(short n)
        {
            bl.Add((byte)(n >> 8 & 0xFF));
            bl.Add((byte)(n & 0xFF));
        }

        public void WriteInt(int n)
        {
            bl.Add((byte)(n >> 24 & 0xFF));
            bl.Add((byte)(n >> 16 & 0xFF));
            bl.Add((byte)(n >> 8 & 0xFF));
            bl.Add((byte)(n & 0xFF));
        }

        public void WriteLong(long n)
        {
            bl.Add((byte)(n >> 56 & 0xFF));
            bl.Add((byte)(n >> 48 & 0xFF));
            bl.Add((byte)(n >> 40 & 0xFF));
            bl.Add((byte)(n >> 32 & 0xFF));
            bl.Add((byte)(n >> 24 & 0xFF));
            bl.Add((byte)(n >> 16 & 0xFF));
            bl.Add((byte)(n >> 8 & 0xFF));
            bl.Add((byte)(n & 0xFF));
        }

        public void WriteFloat(float f)
        {
            byte[] b = new byte[4];
            IntPtr ptr1 = System.Runtime.InteropServices.Marshal.UnsafeAddrOfPinnedArrayElement(b, 0);
            System.Runtime.InteropServices.Marshal.StructureToPtr(f, ptr1, false);
            for (int i = 0; i < 4; i++)
                bl.Add(b[i]);
        }

        public void WriteDouble(double f)
        {
            byte[] b = new byte[8];
            IntPtr ptr1 = System.Runtime.InteropServices.Marshal.UnsafeAddrOfPinnedArrayElement(b, 0);
            System.Runtime.InteropServices.Marshal.StructureToPtr(f, ptr1, false);
            for (int i = 0; i < 8; i++)
                bl.Add(b[i]);
        }

        public void WriteDateTime(DateTime dt)
        {
            WriteLong(dt.Ticks);
        }

        public void WriteString(String s)
        {
            WriteMas(Encoding.Unicode.GetBytes(s));
        }

        public void WriteStringASCII(String s)
        {
            WriteMas(Encoding.ASCII.GetBytes(s));
        }

        public void WriteMas(byte[] b)
        {
            WriteInt(b.Length);
            for (int i = 0; i < b.Length; i++)
                bl.Add(b[i]);
        }

        public void WriteBitMas(bool[] b)
        {
            WriteInt(b.Length);
            int n = 1;
            int actB = 0;
            for (int i = 0; i < b.Length; i++)
            {
                if (b[i]) actB = actB | n;
                n *= 2;
                if (n == 256)
                {
                    n = 1;
                    bl.Add((byte)actB);
                    actB = 0;
                }
            }
            // если есть еще записанные биты
            if (n > 1) bl.Add((byte)actB);
        }

        public void WriteBmp(Bitmap bmp)
        {
            Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
            BitmapData bmpData = bmp.LockBits(rect, ImageLockMode.ReadWrite, bmp.PixelFormat);
            // Get the address of the first line.
            IntPtr ptr = bmpData.Scan0;
            // Declare an array to hold the bytes of the bitmap.
            int bytes = bmpData.Stride * bmp.Height;
            byte[] rgbValues = new byte[bytes];
            // Копируем битмап в массив байт.
            System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes);

            // пишем
            WriteShort((short)bmp.Width);
            WriteShort((short)bmp.Height);
            WriteMas(rgbValues);

            // разблокируем битмап
            bmp.UnlockBits(bmpData);
        }

    }

    class Spliter
    {
        public static List<byte[]> SplitPack(byte idPackForSplit, byte[] buf, int maxSize)
        {
            List<byte[]> bl = new List<byte[]>();
            int pos = 0;
            while (pos < buf.Length)
            {
                int sz = maxSize;
                if ((buf.Length - pos) < (maxSize - 1)) sz = buf.Length - pos + 1;
                byte[] b = new byte[sz];
                b[0] = idPackForSplit;
                for (int i = 1; i < sz; i++)
                    b[i] = buf[i - 1 + pos];
                pos += sz - 1;
                bl.Add(b);
            }
            return bl;
        }

        public static byte[] BuildPack(byte idPackForSplit, List<byte[]> bl, int maxSize)
        {
            int sz = 0;
            for (int i = 0; i < bl.Count; i++)
                sz += bl[i].Length - 1;

            byte[] buf = new byte[sz];
            int pos = 0;
            for (int i = 0; i < bl.Count; i++)
            {
                for (int n = 1; n < bl[i].Length; n++)
                {
                    buf[pos] = bl[i][n];
                    pos++;
                }
            }

            return buf;
        }
    }

    class Compress
    {

    }
}